﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SalesSys.DAL.Models;
using System.Transactions;
using SalesSys.BLL.Sales;
using SalesSys.Comm.Utility;
using SalesSys.DAL;

namespace SalesSys.BLL
{
    /*
     * 担保金问题：
     * 如果账户余额不足，但总额够，则可以担保支付，计入总额担保金
     */

    public class FinanceBLL : BaseBLL<Finance, Guid>
    {
        /// <summary>
        /// 客户帐入款通知订单处理，变化，账户、变化金额
        /// </summary>
        public static Action<Account, decimal> AccountAddManeyOrderHandle;

        /// <summary>
        /// 账户余款有可用，
        /// </summary>
        public static Action<Account, Guid, Guid> AccountBalanceChange;
        /// <summary>
        /// 退款,提交前不要计算退款金额，提交后自动计算退款金额
        /// </summary>
        /// <param name="order"></param>
        /// <param name="money"></param>
        /// <param name="remark"></param>
        /// <returns></returns>
        public bool OrderRefund(Guid orderId, Account account, decimal money, string remark)
        {

            if (money <= 0)
            {
                throw new ArgumentException("退款金额必须大于0");
            }
            OrderBLL orderBll = new OrderBLL();
            var order = orderBll.Details(orderId);
            using (var tran = new TransactionScope())
            {
                //Accounting(money, account, orderId, "计划单退款", remark, null);
               
                Accounting(money, account, order.Creater.Id, order.Customer.Id, orderId, "计划单退款", remark, null);
                //Accounting(money, order.DeliveryAccount, order.Id, "计划单运费退款", remark, null);
                var lockBll = new OrderBLL();
                //lockBll.Edit(order);
                tran.Complete();
            }
            //当前账户余额
            var accountAmount = customerBalanceBll.GetAccountAmount(order.Customer.Id, order.Creater.Id, account.Id);
            if (AccountBalanceChange != null && accountAmount > 0)
            {
                AccountBalanceChange(account, order.Creater.Id, order.Customer.Id);
            }
            return true;

        }

        /// <summary>
        /// 锁价单退款，一次退完剩余金额
        /// </summary>
        /// <param name="order"></param>
        /// <param name="money"></param>
        /// <param name="remark"></param>
        /// <returns></returns>
        public bool LockOrderRefund(LockOrder order, decimal money, string remark)
        {
            if (money <= 0)
            {
                throw new ArgumentException("退款金额必须大于0");
            }
            order.Refund += money;
            using (var tran = new TransactionScope())
            {
                //Accounting(money, order.Account, order.Id, "锁价单退款", remark, null);
                Accounting(money, order.Account, order.Creater.Id, order.Customer.Id, order.Id, "锁价单退款", remark, null);
                var lockBll = new LockOrderBLL();
                lockBll.Edit(order);
                tran.Complete();
            }
            if (AccountBalanceChange != null && order.Account != null && order.Account.Amount > 0)
            {
                AccountBalanceChange(order.Account, order.Creater.Id, order.Customer.Id);
            }
            return true;
        }

        /// <summary>
        /// 订单预付款付款，如可以付则支付，不够则返回实际支付金额
        /// </summary>
        /// <param name="order"></param>
        /// <returns>实际付款金额，为负值表示形成欠款</returns>
        public decimal PaymentOrder(Order order, Employee handler = null)
        {
            ///计算实际可分配付款金额，预付款
            /// --账户扣全款
            /// 记录扣款历史，
            /// 返回分配的付款金额，预付款

            if (order.Total < 0)
            {
                throw new ArgumentException("付款金额不允许负值");
            }
            decimal accountAmount = customerBalanceBll.GetAccountAmount(order.Customer.Id, order.Creater.Id, order.Account.Id);
            var account = accountBLL.Details(order.Account.Id);
            // if (account.Amount <= 0)

            if (accountAmount <= 0)
            {
                return 0;
            }
            var payMoney = order.Total - order.Prepayments;
            if (payMoney <= 0)
            {
                return 0;
            }
            //if (account.Amount >= payMoney)
            if (accountAmount >= payMoney)
            {
                //Accounting(-payMoney, account, order.Id, "订单支付", "", handler);
                Accounting(-payMoney, account, order.Creater.Id, order.Customer.Id, order.Id, "订单支付", "", handler);
                return payMoney;
            }
            else
            {
                //var setPay = account.Amount;
                //Accounting(-setPay, account, order.Id, "订单部分支付", "", handler);
                var setPay = accountAmount;
                Accounting(-setPay, account, order.Creater.Id, order.Customer.Id, order.Id, "订单部分支付", "", handler);
                return setPay;
            }

        }

        public decimal PaymentOrder2(Order order, Employee handler = null, decimal proportion = 1)
        {
            if (Math.Abs(proportion - 1) < 0.01M)
            {
                PaymentOrder(order, handler);
            }
            if (order.Total < 0)
            {
                throw new ArgumentException("付款金额不允许负值");
            }
            decimal accountAmount = customerBalanceBll.GetAccountAmount(order.Customer.Id, order.Creater.Id, order.Account.Id);

            var account = accountBLL.Details(order.Account.Id);
            //if (account.Amount <= 0)
            if (accountAmount <= 0)
            {
                return 0;
            }
            var payMoney = MoneyLocalizable.ToMoney(order.Total * proportion) - order.Prepayments;
            if (payMoney <= 0)
            {
                return 0;
            }

            //if (account.Amount >= payMoney)
            if (accountAmount >= payMoney)
            {
                //Accounting(-payMoney, account, order.Id, "订单按比例支付完成[" + proportion + "]", "", handler);
                Accounting(-payMoney, account, order.Creater.Id, order.Customer.Id, order.Id, "订单按比例支付完成[" + proportion + "]", "", handler);
                return payMoney;
            }
            else
            {
                //var setPay = account.Amount;
                //Accounting(-setPay, account, order.Id, "订单部分支付", "", handler);
                var setPay = accountAmount;
                Accounting(-setPay, account, order.Creater.Id, order.Customer.Id, order.Id, "订单部分支付", "", handler);
                return setPay;
            }

        }

        /// <summary>
        /// 支付运费
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public decimal PaymentOrderDelivery(Order order, Employee handler = null)
        {
            if (order.DeliveryPriceTotal < 0)
            {
                throw new ArgumentException("付款金额不允许负值");
            }
            decimal accountAmount = customerBalanceBll.GetAccountAmount(order.Customer.Id, order.Creater.Id, order.DeliveryAccount.Id);

            var account = accountBLL.Details(order.DeliveryAccount.Id);
            if (accountAmount <= 0)
            {
                return 0;
            }
            var payMoney = order.DeliveryPriceTotal - order.PaidDelivery;
            if (payMoney <= 0)
            {
                return 0;
            }
            if (accountAmount >= payMoney)
            {
                // payMoney = order.Total;
                //Accounting(-payMoney, account, order.Id, "订单运费支付", "", handler);
                Accounting(-payMoney, account, order.Creater.Id, order.Customer.Id, order.Id, "订单运费支付", "", handler);
                return payMoney;
            }
            else
            {
                return 0;
                //payMoney = account.Amount;
            }

        }

        /// <summary>
        /// 锁价单付款
        /// </summary>
        /// <param name="lockOrder"></param>
        /// <returns></returns>
        public bool PayLockOrder(LockOrder lockOrder, Employee handler = null)
        {
            if (lockOrder.Total <= 0)
            {
                throw new ArgumentException("付款金额不允许负值");
            }
            decimal accountAmount = customerBalanceBll.GetAccountAmount(lockOrder.Customer.Id, lockOrder.Creater.Id, lockOrder.Account.Id);

            var account = accountBLL.Details(lockOrder.Account.Id);
            if (accountAmount < (lockOrder.Total))
            {
                return false;
            }
            //Accounting(-lockOrder.Total, account, lockOrder.Id, "锁价单付款", "", handler);
            Accounting(-lockOrder.Total, account, lockOrder.Creater.Id, lockOrder.Customer.Id, lockOrder.Id, "锁价单付款", "", handler);
            return true;
        }

        /// <summary>
        /// 财务确认预付款，此订单能否通过：客户所有账户资金之和-需支付金额>=0
        /// </summary>
        /// <param name="customerId"></param>
        /// <returns></returns>
        public decimal BalanceSum(Guid customerId, Guid empId)
        {
            var sum = BllFactory.GetBll<CustomerBalanceBLL>().GetAccountAllAmount(customerId, empId);
            return sum;
        }

        /// <summary>
        /// 入账,此方法未使用，注意逻辑以不符合需求，不要直接使用
        /// </summary>
        /// <param name="account"></param>
        /// <param name="money"></param>
        private void AddMoney(Account account, decimal money)
        {
            if (money <= 0)
            {
                throw new ArgumentException("入账金额必须大于0");
            }
            if (account.Amount < 0)
            {
                var off = account.Amount;
                var newAmount = account.Amount + money;
                account.Amount = newAmount;
                using (var tran = new TransactionScope())
                {
                    accountBLL.Edit(account);
                    //Accounting(money,account,);
                    if (newAmount < 0)
                    {
                        AccountAddManeyOrderHandle(account, newAmount);
                    }
                    else
                    {
                        AccountAddManeyOrderHandle(account, off);
                    }
                    tran.Complete();
                }
            }
            else
            {
                account.Amount += money;
                accountBLL.Edit(account);
                //AccountBalanceChange(account, order.Creater.Id, order.Customer.Id);
            }

            //加入用户资金
            //首先支付已结算单子
            //再支付预付款单子

        }

        /// <summary>
        /// 结算
        /// </summary>
        /// <param name="order"></param>
        /// <param name="amountDiff">结算</param>
        [Obsolete("结算为业务操作，已移到orderBLL")]
        public void Settlement(Order order, Employee handler = null)
        {
            //应付=实际金额-预付款
            //账户扣除应付
            //
            //再次结算
            //退回实际金额
            //应付=实际金额-预付款
            //账户扣除应付
            //if (order.IsSettlement)
            //{
            //    throw new ArgumentException("已经结算过的订单");
            //}
            var payMoney = order.Total - order.Prepayments;
            var account = accountBLL.Details(order.Account.Id);
            var s = string.Format("应付:{0},已付:{1},本次结算支付{2}", order.Total.ToString("F2"),
                order.Prepayments.ToString("F2"), (-payMoney).ToString("F2"));
            order.IsSettlement = true;
            order.State = OrderState.Settlement;

            Accounting(-payMoney, account, order.Creater.Id, order.Customer.Id, order.Id, "订单结算", s, handler);
        }

        /// <summary>
        /// 对应订单结算后多支付部分的退回余额
        /// </summary>

        public void OrderSettlementBalanceReturned(Order order, Employee handler = null)
        {
            if (order.IsSettlement == false)
            {
                throw new ArgumentException("未结算");
            }
            var re = order.Prepayments - order.Total;
            if (re > 0)
            {
                //Accounting(re, order.Account, order.Id, "订单结算，已支付货款余款付款部分退回");
                Accounting(re, order.Account, order.Creater.Id, order.Customer.Id, order.Id, "订单结算", "已支付货款余款付款部分退回", handler);
                order.Prepayments = order.Total;
            }
            re = order.PaidDelivery - order.DeliveryPriceTotal;
            if (re > 0)
            {
                Accounting(re, order.DeliveryAccount, order.Creater.Id, order.Customer.Id, order.Id, "订单结算", "已支付运费余款退回", handler);
                order.PaidDelivery = order.DeliveryPriceTotal;
            }
        }

        /// <summary>
        /// 结算变更,正值为客户支付，负值为客户收入，order.Total - order.Prepayments;
        /// </summary>
        [Obsolete("不在区分二次结算")]
        //public void SettlementChange(Order order, decimal amountDiff)
        public void SettlementChange(Order order, decimal amountDiff, Employee handler = null)
        {
            if (!order.IsSettlement)
            {
                throw new ArgumentException("订单未结算过，不能申请变更");
            }
            //var payMoney = order.Total - order.Prepayments;
            var account = accountBLL.Details(order.Account.Id);
            var s = string.Format("应付:{0},已付:{1},结算变更支付:{2}", order.Total.ToString("F2"),
                order.Prepayments.ToString("F2"), -amountDiff);
            Accounting(-amountDiff, account, order.Creater.Id, order.Customer.Id, order.Id, "结算", s, handler);
        }

        private PaymentInfoBLL paymentInfoBLL = BllFactory.GetBll<PaymentInfoBLL>();
        private AccountBLL accountBLL = BllFactory.GetBll<AccountBLL>();
        private EmployeeBLL employeeBLL = BllFactory.GetBll<EmployeeBLL>();
        private CustomerBalanceBLL customerBalanceBll = new CustomerBalanceBLL();
        private CustomerBalanceRecordBLL customerBalanceRecordBll = new CustomerBalanceRecordBLL();
        /// <summary>
        /// 报款确认
        /// </summary>
        /// <param name="paymentInfoId"></param>
        /// <param name="money"></param>
        /// <param name="pass"></param>
        /// <param name="remark"></param>
        /// <param name="employeeId"></param>
        public void PaymentInfoConfirm(Guid paymentInfoId, decimal money, bool pass,
            string approvalRemark, Guid employeeId, Guid clientId)
        {
            using (var tran = new TransactionScope())
            {
                var entity = paymentInfoBLL.Details(paymentInfoId);
                if (entity == null || entity.Sum != money)
                {
                    throw new ArgumentException("报款信息无效");
                }
                entity.ApprovalRemark = approvalRemark;
                entity.ApprovalTime = DateTime.Now;
                var emp = employeeBLL.Details(employeeId);
                if (!pass)
                {
                    entity.State = FinaceApprovalState.Disagree;
                    entity.ApprovalRemark = emp.Name + ":" + approvalRemark;

                    paymentInfoBLL.Edit(entity);
                    tran.Complete();
                    return;
                }
                //Accounting(money, entity.Account, entity.Id, "客户经理报款", entity.Remark, emp);
                entity.State = FinaceApprovalState.Agree;
                //entity.Remark = emp.Name + ":" + remark;

                // Rep.Insert(finance);
                //Accounting(money, entity.Account, entity.Id, "客户经理报款", entity.Remark, emp);
                Accounting(money, entity.Account, clientId, entity.Customer.Id, entity.Id, "客户经理报款", entity.Remark, emp);
                paymentInfoBLL.Edit(entity);


                if (AccountBalanceChange != null)
                {
                    AccountBalanceChange(entity.Account, entity.CustomerAgent.Id, entity.Customer.Id);
                }
                tran.Complete();
            }
        }

        static FinanceBLL()
        {
            SequenceApprovalBLL.FinanceRefundApproval.ApprovalEndHandle += FinanceRefundApproval_ApprovalEndHandle;
        }

        /// <summary>
        /// 审批结果处理
        /// </summary>
        /// <param name="paymentInfoId">退款申请id</param>
        /// <param name="bPass">是否通过</param>
        /// <param name="fMoney">金额</param>
        /// <remarks>
        /// 如果通过设置退款申请审批为已通过
        /// </remarks>
        private static void FinanceRefundApproval_ApprovalEndHandle(Guid paymentInfoId, bool bPass, decimal fMoney, string msg)
        {
            var account = new AccountBLL().Details(paymentInfoId);

            PaymentInfoBLL paymentInfoBLL = BllFactory.GetBll<PaymentInfoBLL>();
            var payInfo = paymentInfoBLL.Details(paymentInfoId);

            using (var tran = new TransactionScope())
            {
                if (bPass)
                {
                    payInfo.State = FinaceApprovalState.ApprovePass;
                }
                else
                {
                    payInfo.State = FinaceApprovalState.ApproveRefuse;
                }
                payInfo.ApprovalRemark = msg;

                paymentInfoBLL.Edit(payInfo);
                tran.Complete();
            }
        }

        /// <summary>
        /// 退款申请
        /// </summary>
        /// <param name="accountId"></param>
        /// <param name="money"></param>
        /// <param name="iEmp"></param>
        /// <remarks>
        ///  提交到流程
        ///等待审批结果
        ///如果通过设置审批状态为审批通过
        ///出纳确认退款完成RefundConfirm
        ///退款单状态为已确认
        /// </remarks>
        public void RefundApproval(Guid paymentInfoId, decimal money, IEmployee iEmp)
        {
            PaymentInfoBLL paymentInfoBLL = BllFactory.GetBll<PaymentInfoBLL>();
            var payInfo = paymentInfoBLL.Details(paymentInfoId);
            //查询户头余额
            decimal accountAmount = customerBalanceBll.GetAccountAmount(payInfo.Account.Customer.Id, iEmp.Id, payInfo.Account.Id);
            if (money > accountAmount)
            {
                throw new ArgumentException("超出余额");
            }
            var emp = employeeBLL.Details(iEmp.Id);
            var appr = new SequenceApprovalBLL.FinanceRefundApproval();
            appr.Submit(payInfo, money, emp);
        }


        private static object lockObj = new object();

        /// <summary>
        /// 账户变动入口，不得直接编辑账户金额,正值表示入账，负表示出帐
        /// </summary>
        /// <param name="money"></param>
        /// <param name="account">户头</param>
        /// <param name="employeeId">客户经理</param>
        /// <param name="customerId">客户</param>
        /// <param name="sourceId"></param>
        /// <param name="sourceInfo"></param>
        /// <param name="remark"></param>
        /// <param name="handler"></param>
        //private void Accounting(decimal money, Account account, Guid sourceId, string sourceInfo, string remark = null,
        private void Accounting(decimal money, Account account, Guid employeeId, Guid customerId, Guid sourceId, string sourceInfo, string remark = null,
            Employee handler = null)
        {
            if (money < 0 && paymentInfoBLL.CheckExecuting(account.Id, customerId, employeeId, PaymentStatus.Refund))
                throw new ArgumentException("该账户正在退款审批中，不允许支付。");

            lock (lockObj)
            {

                using (var tran = new TransactionScope())
                {
                    DateTime dtChange = DateTime.Now;
                    //修改客户余额表
                    CustomerBalance customerBalance = customerBalanceBll.GetAccountAmountInfo(customerId, employeeId, account.Id);
                    customerBalance.Balance = customerBalance.Balance + money;
                    customerBalance.AccountId = account.Id;
                    customerBalance.EmployeeId = employeeId;
                    customerBalance.CustomerId = customerId;
                    //先行判断该客户在余额表中是否存在
                    if (!customerBalanceBll.IsExistAccountAmount(customerId, employeeId, account.Id))
                    {
                        customerBalance.Id = Guid.NewGuid();
                        customerBalanceBll.Create(customerBalance);
                    }
                    else
                    {
                        customerBalanceBll.Edit(customerBalance);
                    }

                    //添加客户户头流水
                    CustomerBalanceRecord customerBalanceRecord = new CustomerBalanceRecord();
                    customerBalanceRecord.Id = Guid.NewGuid();
                    customerBalanceRecord.AccountId = account.Id;
                    customerBalanceRecord.EmployeeId = employeeId;
                    customerBalanceRecord.CustomerId = customerId;
                    customerBalanceRecord.AfterBalance = customerBalance.Balance;
                    customerBalanceRecord.BeforeBalance = customerBalance.Balance - money;
                    customerBalanceRecord.ChangeBalance = money;
                    customerBalanceRecord.ChangeResult = sourceInfo;
                    customerBalanceRecord.ChangeTime = dtChange;
                    customerBalanceRecord.Remark = remark;
                    if (handler != null)
                    {
                        customerBalanceRecord.HandlerId = handler.Id;
                    }
                    customerBalanceRecordBll.Create(customerBalanceRecord);

                    var finance = new Finance();
                    finance.Account = account;
                    finance.AfterAmount = customerBalance.Balance;
                    finance.BeforeAmount = customerBalance.Balance - money;
                    finance.CreateTime = dtChange;
                    finance.Handler = handler;
                    finance.Money = money;
                    finance.SourceInfo = sourceInfo;
                    finance.SourceTag = "" + sourceId;
                    finance.Remark = remark;
                    account.Amount = customerBalance.Balance;
                    Rep.Insert(finance);
                    accountBLL.Edit(account);
                    tran.Complete();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="paymentInfoId"></param>
        /// <param name="money"></param>
        /// <param name="pass"></param>
        /// <param name="p"></param>
        /// <param name="guid"></param>
        /// <remarks>
        /// 
        /// </remarks>
        ///        
        public void RefundConfirm(Guid paymentInfoId, decimal money, bool pass, string approvalRemark,
            Guid employeeId)
        {

            var entity = paymentInfoBLL.Details(paymentInfoId);
            if (entity == null || entity.Sum != money)
            {
                throw new ArgumentException("退款信息无效");
            }
            entity.ApprovalRemark = approvalRemark;
            entity.ApprovalTime = DateTime.Now;
            var emp = employeeBLL.Details(employeeId);
            if (!pass)
            {
                entity.State = FinaceApprovalState.Disagree;
                entity.ApprovalRemark = emp.Name + ":" + approvalRemark;

                paymentInfoBLL.Edit(entity);
                return;
            }
            entity.State = FinaceApprovalState.Agree;
            using (var tran = new TransactionScope())
            {
                // Rep.Insert(finance);
                //Accounting(-money, entity.Account, entity.Id, "客户经理退款", entity.Remark, emp);
                Accounting(-money, entity.Account, entity.CustomerAgent.Id, entity.Customer.Id, entity.Id, "客户经理退款", entity.Remark, emp);
                paymentInfoBLL.Edit(entity);
                tran.Complete();
            }
            if (AccountBalanceChange != null)
            {
                AccountBalanceChange(entity.Account, entity.CustomerAgent.Id, entity.Customer.Id);
            }
        }

        /// <summary>
        /// 获取客户流水
        /// </summary>
        /// <param name="customerId"></param>
        /// <param name="accountId"></param>
        /// <param name="page"></param>
        /// <param name="rows"></param>
        /// <returns></returns>
        public KeyValuePair<int, IEnumerable<Finance>> GetFinanceList(Guid customerId, Guid? accountId = null,
            int page = 1, int rows = 10)
        {
            var list = Rep.Entities;
            if (accountId != null)
            {
                list =
                    list.Where(p => p.Account.Id == accountId && p.Money != 0);
                //list =  list.OrderByDescending(p => p.CreateTime).ThenByDescending(p => p.Money).Paging(page,rows) ;
                list = list.OrderByDescending(p => p.CreateTime).ThenByDescending(p => p.Money).Paging(page, rows);
                int total = list.Count();
                return new KeyValuePair<int, IEnumerable<Finance>>(total, list.ToArray());
            }
            else
            {
                list =
                list.Where(p => p.Account.CustomerId == customerId && p.Money != 0);
                list = list.OrderByDescending(p => p.CreateTime).ThenByDescending(p => p.Money).Paging(page, rows);
                int total = list.Count();
                return new KeyValuePair<int, IEnumerable<Finance>>(total, list.ToArray());
            }
        }
    }
}
